// -----------------------------------------------------------------------
// <copyright file="ContactPersonManager.cs" company="Imar.Spaanjaars.Com">
//   Copyright 2008 - 2009 - Imar.Spaanjaars.Com. All rights reserved.
// </copyright>
// -----------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Security;
using System.Security.Permissions;
using System.Transactions;
using Spaanjaars.ContactManager.BusinessEntities;
using Spaanjaars.ContactManager.Dal;
using Spaanjaars.Validation;

namespace Spaanjaars.ContactManager.Bll
{
	/// <summary>
	/// The ContactPersonManager class is responsible for managing BusinessEntities.ContactPerson objects in the system.
	/// </summary>
	[DataObjectAttribute()]
	public static class ContactPersonManager
	{
		#region Public Methods

		/// <summary>
		/// Gets a list with all ContactPerson objects in the database.
		/// </summary>
		/// <returns>A list with all contact persons from the database when the database contains any contact person, or null otherwise.</returns>
		[DataObjectMethod(DataObjectMethodType.Select, true)]
		public static ContactPersonCollection GetList()
		{
			return GetList(string.Empty, -1, -1);
		}

		/// <summary>
		/// Gets a list with all ContactPerson objects in the database.
		/// </summary>
		/// <param name="sortExpression">A string containing the expression to sort on. Can contain desc to indicate descending order.</param>
		/// <returns>
		/// A list with all contact persons from the database when the database contains any contact person, or null otherwise.
		/// </returns>
		[DataObjectMethod(DataObjectMethodType.Select, false)]
		public static ContactPersonCollection GetList(string sortExpression)
		{
			return GetList(sortExpression, -1, -1);
		}

		/// <summary>
		/// Gets a single page with ContactPerson objects in the database.
		/// </summary>
		/// <param name="startRowIndex">Start index of the first record in the result set.</param>
		/// <param name="maximumRows">The size of the page; e.g. the maximum number of records you want to return.</param>
		/// <returns>
		/// A list with contact persons from the database when the database contains any contact person, or an empty otherwise.
		/// The list is limited to the currently requested page size.
		/// </returns>
		[DataObjectMethod(DataObjectMethodType.Select, false)]
		public static ContactPersonCollection GetList(int startRowIndex, int maximumRows)
		{
			return GetList(string.Empty, startRowIndex, maximumRows);
		}

		/// <summary>
		/// Gets a list with all ContactPerson objects in the database based on the custom search criteria.
		/// </summary>
		/// <param name="sortExpression">A criteria object containing the criteria to search for.</param>
		/// <returns>
		/// A list with all contact persons from the database matching the criteria, or an empty list otherwise.
		/// </returns>
		[DataObjectMethod(DataObjectMethodType.Select, false)]
		public static ContactPersonCollection GetList(ContactPersonCriteria contactPersonCriteria)
		{
			return ContactPersonDB.GetList(contactPersonCriteria);
		}

		/// <summary>
		/// Gets a list with all ContactPerson objects in the database.
		/// </summary>
		/// <param name="sortExpression">A string containing the expression to sort on. Can contain desc to indicate descending order.</param>
		/// <returns>
		/// A list with all contact persons from the database when the database contains any contact person, or null otherwise.
		/// </returns>
		[DataObjectMethod(DataObjectMethodType.Select, false)]
		public static ContactPersonCollection GetList(string sortExpression, int startRowIndex, int maximumRows)
		{
			ContactPersonCollection myCollection = ContactPersonDB.GetList();
			if (!string.IsNullOrEmpty(sortExpression))
			{
				myCollection.Sort(new ContactPersonComparer(sortExpression));
			}
			if (startRowIndex >= 0 && maximumRows > 0)
			{
				return new ContactPersonCollection(myCollection.Skip(startRowIndex).Take(maximumRows).ToList());
			}
			return myCollection;
		}

		/// <summary>
		/// Gets the number of records in the database.
		/// </summary>
		/// <returns></returns>
		public static int SelectCountForGetList()
		{
			return ContactPersonDB.SelectCountForGetList();
		}

		/// <summary>
		/// Gets a single ContactPerson from the database without its contact data.
		/// </summary>
		/// <param name="id">The ID of the contact person in the database.</param>
		/// <returns>A ContactPerson object when the ID exists in the database, or <see langword="null"/> otherwise.</returns>
		[DataObjectMethod(DataObjectMethodType.Select, false)]
		public static ContactPerson GetItem(int id)
		{
			return GetItem(id, false);
		}

		/// <summary>
		/// Gets a single ContactPerson from the database.
		/// </summary>
		/// <param name="id">The ID of the contact person in the database.</param>
		/// <param name="getContactRecords">Determines whether to load all associated contact records as well.</param>
		/// <returns>
		/// A ContactPerson object when the ID exists in the database, or <see langword="null"/> otherwise.
		/// </returns>
		[DataObjectMethod(DataObjectMethodType.Select, false)]
		public static ContactPerson GetItem(int id, bool getContactRecords)
		{
			ContactPerson myContactPerson = ContactPersonDB.GetItem(id);
			if (myContactPerson != null && getContactRecords)
			{
				myContactPerson.Addresses = AddressDB.GetList(id);
				myContactPerson.EmailAddresses = EmailAddressDB.GetList(id);
				myContactPerson.PhoneNumbers = PhoneNumberDB.GetList(id);
			}
			return myContactPerson;
		}

		/// <summary>
		/// Saves a contact person in the database.
		/// </summary>
		/// <param name="myContactPerson">The ContactPerson instance to save.</param>
		/// <returns>The new ID if the ContactPerson is new in the database or the existing ID when an item was updated.</returns>
		[DataObjectMethod(DataObjectMethodType.Update, true)]
		public static bool Save(ContactPerson myContactPerson)
		{
			if (!myContactPerson.Validate())
			{
				throw new InvalidSaveOperationException("Can't save an invalid Person. Please make sure Validate() returns true before you call Save.");
			}
			using (TransactionScope myTransactionScope = new TransactionScope())
			{
				int contactPersonId = ContactPersonDB.Save(myContactPerson);

				foreach (Address myAddress in myContactPerson.Addresses)
				{
					myAddress.ContactPersonId = contactPersonId;
					AddressDB.Save(myAddress);
				}

				foreach (EmailAddress myEmailAddress in myContactPerson.EmailAddresses)
				{
					myEmailAddress.ContactPersonId = contactPersonId;
					EmailAddressDB.Save(myEmailAddress);
				}

				foreach (PhoneNumber myPhoneNumber in myContactPerson.PhoneNumbers)
				{
					myPhoneNumber.ContactPersonId = contactPersonId;
					PhoneNumberDB.Save(myPhoneNumber);
				}

				//  Assign the ContactPerson its new (or existing ID).
				myContactPerson.Id = contactPersonId;

				myTransactionScope.Complete();

				return true;
			}
		}

		/// <summary>
		/// Deletes a contact person from the database.
		/// </summary>
		/// <param name="myContactPerson">The ContactPerson instance to delete.</param>
		/// <returns>Returns <c>true</c> when the object was deleted successfully, or <c>false</c> otherwise.</returns>
		[DataObjectMethod(DataObjectMethodType.Delete, true)]
		public static bool Delete(ContactPerson myContactPerson)
		{
			try
			{
				PrincipalPermission myPrincipalPermission = new PrincipalPermission(null, "Administrators");
				myPrincipalPermission.Demand();
				return ContactPersonDB.Delete(myContactPerson.Id);
			}
			catch (SecurityException)
			{
				// TODO: Log error here
				throw;
			}
		}

		#endregion

		#region IComparable

		/// <summary>
		/// Implements IComparer&lt;ContactPerson&gt; to allow ContactPerson objects to be sorted.
		/// </summary>
		private class ContactPersonComparer : IComparer<ContactPerson>
		{
			private string _sortColumn;
			private bool _reverse;
			/// <summary>
			/// Constructor for the ContactPersonComparer class that expects the property of the ContactPerson class to sort on.
			/// </summary>
			/// <param name="sortExpression">Contains the property of the ContactPerson class to sort on. Append [space]desc to sort in reversed order.</param>
			public ContactPersonComparer(string sortExpression)
			{
				if (string.IsNullOrEmpty(sortExpression))
				{
					sortExpression = "Id desc";
				}
				_reverse = sortExpression.ToUpperInvariant().EndsWith(" desc", StringComparison.OrdinalIgnoreCase);
				if (_reverse)
				{
					_sortColumn = sortExpression.Substring(0, sortExpression.Length - 5);
				}
				else
				{
					_sortColumn = sortExpression;
				}
			}

			/// <summary>
			/// Compares two instances of ContactPerson.
			/// </summary>
			/// <param name="x">The left hand side ContactPerson object.</param>
			/// <param name="y">The right hand side ContactPerson object.</param>
			public int Compare(ContactPerson x, ContactPerson y)
			{
				int retVal = 0;
				switch (_sortColumn.ToUpperInvariant())
				{
					case "ID":
						retVal = x.Id.CompareTo(y.Id);
						break;
					case "FIRSTNAME":
						retVal = string.Compare(x.FirstName, y.FirstName, StringComparison.OrdinalIgnoreCase);
						break;
					case "MIDDLENAME":
						retVal = string.Compare(x.MiddleName, y.MiddleName, StringComparison.OrdinalIgnoreCase);
						break;
					case "LASTNAME":
						retVal = string.Compare(x.LastName, y.LastName, StringComparison.OrdinalIgnoreCase);
						break;
					case "FULLNAME":
						retVal = string.Compare(x.FullName, y.FullName, StringComparison.OrdinalIgnoreCase);
						break;
					case "DATEOFBIRTH":
						retVal = DateTime.Compare(x.DateOfBirth, y.DateOfBirth);
						break;
					case "TYPE":
						retVal = x.Type.CompareTo(y.Type);
						break;
				}
				int _reverseInt = 1;
				if ((_reverse))
				{
					_reverseInt = -1;
				}
				return (retVal * _reverseInt);
			}
		}
		#endregion
	}
}